#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8

import random
from functools import reduce
from math import *
from collections import Counter
import matplotlib.pyplot as plt


class Stats:
    stats_fields = [
        'min', 'max', 'range',
        's', 'mean',
        'variance', 'sd',
        'mod', 'median', ]

    def __init__(self, datas):
        self.datas = list(datas)
        self.n = len(self.datas)

    def stats_value(self):
        ''' 计算各种统计量 '''
        min_ = min(self.datas)
        max_ = max(self.datas)
        range = max_ - min_
        s = sum(self.datas)
        mean = s / self.n

        v_ele = lambda ele: fabs(ele - mean) ** 2
        # def v_ele(ele):
        #     v= fabs(ele - mean) ** 2
        #     print('ele_v', ele, v)
        #     return v

        # variance = reduce(lambda x, y: v_ele(x) + v_ele(y), self.datas)
        variance_ele = map(v_ele, self.datas)
        variance = sum(variance_ele) / self.n
        sd = sqrt(variance)

        return  {
            'n' : self.n,
            'min' : min_,
            'max' : max_,
            'range' : range,
            'mid_range' : (min_ + max_) / 2,
            'sum' : s,
            'mean' : mean,
            'variance' : variance,
            'sd' : sd,
        }


class CentralLimit:
    def __init__(self, population_n, sample_n):
        self.pop_n = population_n
        self.sample_n = sample_n

        # 从n倍数据中弄个population出来, 随意即可.
        # parent_pop_n = 1
        self.population = random.sample(range(self.pop_n * 1), self.pop_n)

        pop_stat = Stats(self.population).stats_value()
        self.pop_mean = pop_stat['mean']
        self.pop_sd   = pop_stat['sd']

        self.simulate_n = 10 ** 4 # 抽样/试验 次数

    def _sample_experiment(self):
        samples = random.sample(self.population, self.sample_n)
        samples_stat = Stats(samples).stats_value()

        sample_mean  = samples_stat['mean']
        sample_sd    = samples_stat['sd']
        return sample_mean

    def simulate(self):
        # TODO 计算mean 结果的平均值和variance
        mean_list = []
        records = Counter()
        for i in range(self.simulate_n):
            sample_mean = self._sample_experiment()
            records[sample_mean] += 1
            mean_list.append(sample_mean)

        # print(records)
        # return
        # print(list(records.keys()))
        # print(list(records.values()))
        # print(list(records.values()))
        mean_list_stat = Stats(mean_list).stats_value()
        mean_mean = mean_list_stat['mean']
        mean_sd = mean_list_stat['sd']
        print('pop', self.pop_mean, self.pop_sd)
        print('mean_mean', mean_mean, mean_sd, self.pop_sd/sqrt(self.sample_n))

        keys = records.keys()
        values = [n/self.sample_n for n in records.values()]
        # values = records.values()
        return keys, values


def simulate():
    c = CentralLimit(1000, 500)
    xs, ys = c.simulate()
    plt.scatter(xs, ys)
    plt.show()


def main():
    print("start main")
    simulate()

if __name__ == "__main__":
    main()
